/* This is the updated version of MikuButton Project with Slider Bar support
 * It use 2 MPR121 touch sensor to detect touches.
 * Once touch is detected, it will send keycode as buttons.
 * The Slider buttons are still working at the same time.
 */
 
#include <USBKeyboardMiku.h>
#include <Wire.h>
#include "Adafruit_MPR121.h"

#ifndef _BV
#define _BV(bit) (1 << (bit)) 
#endif

#define LAYOUT LAYOUT_US

// Setup Miku button mapping
#define TRANGLE   13
#define SQUARE    12
#define CROSS     11
#define CIRCLE    10

#define START     9

#define L_S_LEFT  A3
#define L_S_RIGHT A2
#define R_S_LEFT  A1
#define R_S_RIGHT A0

const uint8_t KEY_ARRAY[9] = { TRANGLE,SQUARE,CROSS,CIRCLE,START,L_S_LEFT,L_S_RIGHT,R_S_LEFT,R_S_RIGHT };
const uint8_t KEY_CODES[9] = { 26,4,22,7,40,20,8,24,18 }; //Keyboard code mapping W,A,S,D,ENTER,Q,E,U,O
uint8_t KEY_BUFFER_LAST[9] = { 0,0,0,0,0,0,0,0,0 };
uint8_t KEY_BUFFER_NOW[9] = { 0,0,0,0,0,0,0,0,0 };
uint8_t HID_BUFFER[6] = { 0,0,0,0,0,0 };

bool updateReport = false;

uint32_t LAST_TOUCH_READ = 0;
uint32_t CURR_TOUCH_READ = 0;

uint32_t ONE = 1;

bool sliderTouched = false;

// To tune the sensitivity of the slider bar
// The higher number, the slower allowed your movement
uint8_t checkCount = 0; // Slider check counter
uint8_t COUNT_MAX = 1;  // Maximum allowed checking interval
    
USBKeyboard mKeyboard = USBKeyboard();

// Assume 2 slider touch sensor used, left and right
Adafruit_MPR121 cap_l = Adafruit_MPR121();
Adafruit_MPR121 cap_r = Adafruit_MPR121();

void setup() 
{
  // Init Miku button pins
  initButtonPins();

  // Init MPR121 slider bar touch sensors
  initSliderbar();
  
  // Disable TIMER interrupt
  //TIMSK0 = 0; //For Atmega328p
  TIMSK = 0; //For Atmega8
  
}

void initSliderbar() 
{
  // Default address is 0x5A, if tied to 3.3V its 0x5B
  // If tied to SDA its 0x5C and if SCL then 0x5D
  if (!cap_l.begin(0x5A)) {
    while (1);
  }
  cap_l.setThresholds(18, 9); // Default value is 12, 6. Mine is too sensitive, change to 24, 12 to reduce sensitivity
  
  if (!cap_r.begin(0x5B)) {
    while (1);
  }
  cap_r.setThresholds(18, 9);  // Default value is 12, 6. Mine is too sensitive, change to 24, 12 to reduce sensitivity
}

void initButtonPins() 
{
  for (uint8_t i = 0; i < 9; ++i)
  {
    pinMode(KEY_ARRAY[i], INPUT_PULLUP);
  }
}

void loop() 
{
  // put your main code here, to run repeatedly:

  // Keep USB live
  mKeyboard.update();
  
  // Read all key state
  readButtonState();

  // Read slider bar state
  if (checkCount == 0) // Only check it when counter is 0
  {
    readSliderbarState();
  }
  
  // Check key press changed or not
  keyPressChanged();
  
  // Send key to HID
  if (updateReport)
  {
    sendKeyCodeToHID();
  }
  
  checkCount++; // Increase the counter
  if (checkCount >= COUNT_MAX) // Reach the maximum allow counter value, reset the counter
  {
    checkCount = 0;
  } 
  
  delayMicroseconds(20000); // Delay 20 us  USB HID communication
}

// Read button states
void readButtonState() 
{  
  for (uint8_t i = 0; i < 9; ++i) // Loop through each button pins, read button states
  {
    bool buttonState = digitalRead(KEY_ARRAY[i]);

    if (buttonState == LOW)
    {
      KEY_BUFFER_NOW[i] = 1;
    }
    else
    {
      KEY_BUFFER_NOW[i] = 0;
    }
  }
}

// Read slider bar state
void readSliderbarState()
{
  uint32_t tempTouchReadLeft = cap_l.touched();  // Read left slider bar sensor
  uint16_t tempTouchReadRight = cap_r.touched(); // Read right slider bar sensor

  // Combine left and right touch read
  CURR_TOUCH_READ = tempTouchReadLeft << 16 | tempTouchReadRight << 4;

  if (CURR_TOUCH_READ > 0) // Slider bar touched
  {
    if (sliderTouched) // Only calculate when slider is already touched
    {
      if (tempTouchReadLeft & _BV(11)) // The most left sensor is detected, send left left
      {
        KEY_BUFFER_NOW[5] = 1;
      }
      else if (tempTouchReadRight & _BV(0)) // The most right sensor is detected, send right right
      {
        KEY_BUFFER_NOW[8] = 1;
      }
      else if (LAST_TOUCH_READ > 0 && CURR_TOUCH_READ > 0) // Sliding, where both previous read and current read are not 0
      {
        if (CURR_TOUCH_READ < LAST_TOUCH_READ)
        {
          // Slide right detected, will use Left Slider Right
          KEY_BUFFER_NOW[6] = 1;

          // Detect whether this is a second slide input
          // As slide right detected, the other one can only be left

          for (uint8_t i = 4; i < 28; ++i)
          {
            if ((LAST_TOUCH_READ & (ONE << i)) && !(CURR_TOUCH_READ & (ONE << i)))
            {
              // Slide left detected, set Right Slider Left
              KEY_BUFFER_NOW[7] = 1;
              break; // No need to keep on loop as left detected
            }
            else if (!(LAST_TOUCH_READ & (ONE << i)) && (CURR_TOUCH_READ & (ONE << i)))
            {
              // Slide right detected, set Right Slider Right
              KEY_BUFFER_NOW[8] = 1;
              break; // No need to keep on loop as left detected
            }
          }

        }
        else if (CURR_TOUCH_READ > LAST_TOUCH_READ)
        {
          // Slide left detected, will use Left Slider Left
          KEY_BUFFER_NOW[5] = 1;

          // Detect whether this is a second slide input
          // As slide left detected, the other one can only be right
          
          for (uint8_t i = 4; i < 28; ++i)
          {
            if (!(LAST_TOUCH_READ & (ONE << i)) && (CURR_TOUCH_READ & (ONE << i)))
            {
              // Slide right detected, set Right Slider Right
              KEY_BUFFER_NOW[8] = 1;
              break; // No need to keep on loop as left detected
            }
            else if ((LAST_TOUCH_READ & (ONE << i)) && !(CURR_TOUCH_READ & (ONE << i)))
            {
              // Slide left detected, set Right Slider left
              KEY_BUFFER_NOW[7] = 1;
              break; // No need to keep on loop as left detected
            }
          }

        }
      }
    }

    sliderTouched = true; // When current read is not 0, slider bar is touched
  }
  else
  {
    // Slider bar is not touched, clear all the key buffers
    for (uint8_t i = 5; i < 9; ++i)
    {
      KEY_BUFFER_NOW[i] = 0; 
    }
    sliderTouched = false;
  }

  LAST_TOUCH_READ = CURR_TOUCH_READ; // Refresh last read values with current read values
}

// Check whether there are any key press changes
// As slider bar is mapping to the key as well, it will also check slider bar touch changes
void keyPressChanged()
{
  for (uint8_t i = 0; i < 9; ++i) // Go through each key buffer, if buffer are change, set flag to send changes through USB HID
  {
    if (KEY_BUFFER_NOW[i] != KEY_BUFFER_LAST[i])
      updateReport = true;

    KEY_BUFFER_LAST[i] = KEY_BUFFER_NOW[i]; // Refresh last key buffer
  }
}

// Send key code through USB HID
void sendKeyCodeToHID() {
  uint8_t idx = 0;
  
  for (uint8_t i = 0; i < 9; ++i)     // HID keyboard support maximum 6 keys at the same time
  {                                   // For PDAFT, the maximum keys will be "TRANGLE+SQUARE+CROSS+CIRCLE+LEFT SLIDER+RIGHT SLIDER"
    if (KEY_BUFFER_NOW[i] == 1)       // So 6 keys are enough
    {
      HID_BUFFER[idx] = KEY_CODES[i];
      ++idx;
    }

    if (idx >5) // It should not exceed 6 keys, this is just a false safe
      break;
  }

  // Send HID report via USB
  mKeyboard.sendReport(0,HID_BUFFER[0],HID_BUFFER[1],HID_BUFFER[2],HID_BUFFER[3],HID_BUFFER[4],HID_BUFFER[5]);

  // Clear HID Buffer
  for (uint8_t i = 0; i < 6; ++i)
  {
    HID_BUFFER[i] = 0;
  }
}
